package com.baidu.iit.pxp.el.juel.ast;

import com.baidu.iit.pxp.el.ELContext;
import com.baidu.iit.pxp.el.ELException;
import com.baidu.iit.pxp.el.MethodInfo;
import com.baidu.iit.pxp.el.ValueReference;
import com.baidu.iit.pxp.el.juel.Bindings;
import com.baidu.iit.pxp.el.juel.MethodNotFoundException;
import com.baidu.iit.pxp.el.juel.Node;
import com.baidu.iit.pxp.el.juel.PropertyNotFoundException;

/**
 * User: huangweili
 * Date: 14-4-29
 * Time: 下午4:45
 */
public class AstMethod extends AstNode {
    private final AstProperty property;
    private final AstParameters params;

    public AstMethod(AstProperty property, AstParameters params) {
        this.property = property;
        this.params = params;
    }

    public boolean isLiteralText() {
        return false;
    }

    public Class<?> getType(Bindings bindings, ELContext context) {
        return null;
    }

    public boolean isReadOnly(Bindings bindings, ELContext context) {
        return true;
    }

    public void setValue(Bindings bindings, ELContext context, Object value) {
        throw new ELException(String.format("Cannot set rvalue value %s", getStructuralId(bindings)));
    }

    public MethodInfo getMethodInfo(Bindings bindings, ELContext context, Class<?> returnType, Class<?>[] paramTypes) {
        return null;
    }

    public boolean isLeftValue() {
        return false;
    }

    public boolean isMethodInvocation() {
        return true;
    }

    public final ValueReference getValueReference(Bindings bindings, ELContext context) {
        return null;
    }

    @Override
    public void appendStructure(StringBuilder builder, Bindings bindings) {
        property.appendStructure(builder, bindings);
        params.appendStructure(builder, bindings);
    }

    @Override
    public Object eval(Bindings bindings, ELContext context) {
        return invoke(bindings, context, null, null, null);
    }

    public Object invoke(Bindings bindings, ELContext context, Class<?> returnType, Class<?>[] paramTypes, Object[] paramValues) {
        Object base = property.getPrefix().eval(bindings, context);
        if (base == null) {
            throw new PropertyNotFoundException(String.format("Cannot access method %s", property.getPrefix()));
        }
        Object method = property.getProperty(bindings, context);
        if (method == null) {
            throw new PropertyNotFoundException(String.format("Cannot find method %s in %s", "null", base));
        }
        String name = bindings.convert(method, String.class);
        paramValues = params.eval(bindings, context);

        context.setPropertyResolved(false);
        Object result = context.getELResolver().invoke(context, base, name, paramTypes, paramValues);
        if (!context.isPropertyResolved()) {
            throw new MethodNotFoundException(String.format("Cannot find method %s in %s", name, base.getClass()));
        }
        return result;
    }

    public int getCardinality() {
        return 2;
    }

    public Node getChild(int i) {
        return i == 0 ? property : i == 1 ? params : null;
    }

    @Override
    public String toString() {
        return "<method>";
    }
}
